package com.fm.webrtc.connection;

import static org.webrtc.PeerConnection.SignalingState.HAVE_REMOTE_OFFER;

import android.content.Context;
import android.util.Log;

import com.fm.mediasoup.callback.CallbackMediaStream;
import com.fm.mediasoup.callback.CallbackPeerMessage;
import com.fm.webrtc.MainActivity;
import com.fm.webrtc.message.CandidateMessage;
import com.fm.webrtc.message.SdpMessage;
import com.fm.mediasoup.util.Camera;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.*;
import org.webrtc.audio.JavaAudioDeviceModule;

import java.util.ArrayList;
import java.util.List;

public class PeerConnection implements org.webrtc.PeerConnection.Observer,SdpObserver, CallbackPeerMessage {

    private PeerConnectionFactory.InitializationOptions initializationOptions;
    private Context context;
    private int width = 640;
    private int hegiht = 480;
    private int fps = 20;
    private int peerId = 0;
    private static PeerConnection peerConnectionObject = null;
    private ArrayList<org.webrtc.PeerConnection.IceServer> iceServerList;
    private org.webrtc.PeerConnection.IceServer iceServer;
    private PeerConnectionFactory peerConnectionFactory;
    private org.webrtc.PeerConnection peerConnection;
    private static final String stun = "stun:stun.l.google.com:19302";
    private org.webrtc.PeerConnection.RTCConfiguration rtcConfig;
    private MediaStream mediaStream;
    private SurfaceTextureHelper surfaceTextureHelper;
    private EglBase.Context eglBaseContext;
    private  SurfaceViewRenderer svrLocal;
    private SurfaceViewRenderer remoteVideo;

    private CallbackMediaStream callbackMediaStream;

    private PeerConnection(Context context, SurfaceTextureHelper surfaceTextureHelper, EglBase.Context eglBaseContext, SurfaceViewRenderer svrLocal, CallbackMediaStream callbackMediaStream){
        this.context = context;
        this.surfaceTextureHelper = surfaceTextureHelper;
        this.eglBaseContext = eglBaseContext;
        this.svrLocal = svrLocal;
        this.callbackMediaStream = callbackMediaStream;
    }

    public static PeerConnection getInstance(Context context, SurfaceTextureHelper surfaceTextureHelper, EglBase.Context eglBaseContext, SurfaceViewRenderer localVideo, CallbackMediaStream callbackMediaStream) {
        if (peerConnectionObject == null) {
            peerConnectionObject = new PeerConnection(context, surfaceTextureHelper, eglBaseContext, localVideo,callbackMediaStream );
        }
        return peerConnectionObject;
    }

    private void initPeerConnection(){
        initializationOptions = PeerConnectionFactory.InitializationOptions.builder(this.context)
                .setEnableInternalTracer(true)
                .createInitializationOptions();

        PeerConnectionFactory.initialize(initializationOptions);

        JavaAudioDeviceModule audioDeviceModule = JavaAudioDeviceModule.builder(this.context).createAudioDeviceModule();

        peerConnectionFactory = PeerConnectionFactory.builder()
                .setAudioDeviceModule(audioDeviceModule)
                .setVideoDecoderFactory(new DefaultVideoDecoderFactory(this.eglBaseContext))
                .setVideoEncoderFactory(new DefaultVideoEncoderFactory(this.eglBaseContext, true, true))
                .createPeerConnectionFactory();



        iceServerList = new ArrayList<>();

        iceServer = org.webrtc.PeerConnection.IceServer.builder(stun).createIceServer();
        iceServerList.add(iceServer);

        rtcConfig = new org.webrtc.PeerConnection.RTCConfiguration(iceServerList);
        rtcConfig.sdpSemantics = org.webrtc.PeerConnection.SdpSemantics.UNIFIED_PLAN;

        List<String> streamList = new ArrayList<>();
        streamList.add("stream_id");


        PeerConnectionDependencies peerConnectionDependencies = PeerConnectionDependencies.builder(this).createPeerConnectionDependencies();

        peerConnection = peerConnectionFactory.createPeerConnection(rtcConfig, peerConnectionDependencies);


        AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("audio_label", peerConnectionFactory.createAudioSource(new MediaConstraints()));

        audioTrack.setEnabled(true);
        audioTrack.setVolume(10);
        //mediaStream.addTrack(audioTrack);
        peerConnection.addTrack(audioTrack, streamList);


        VideoCapturer videoCapturer = Camera.createCameraCapturer(new Camera1Enumerator(false));
        if (videoCapturer != null) {
            VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
            VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("video_label", videoSource);
            videoTrack.setEnabled(true);
            videoTrack.addSink(svrLocal);

            videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
            videoCapturer.startCapture(width, hegiht, fps);

            peerConnection.addTrack(videoTrack, streamList);
        }

//        peerConnection.createOffer(this, new MediaConstraints());

    }

    @Override
    public void onSignalingChange(org.webrtc.PeerConnection.SignalingState signalingState) {
        Log.e("fuweida", "onSignalingChange");
    }

    @Override
    public void onIceConnectionChange(org.webrtc.PeerConnection.IceConnectionState iceConnectionState) {
        Log.e("fuweida", "onIceConnectionChange");
    }

    @Override
    public void onIceConnectionReceivingChange(boolean b) {
        Log.e("fuweida", "onIceConnectionReceivingChange");
    }

    @Override
    public void onIceGatheringChange(org.webrtc.PeerConnection.IceGatheringState iceGatheringState) {
        Log.e("fuweida", "onIceGatheringChange");

    }

    @Override
    public void onIceCandidate(IceCandidate iceCandidate) {
        Log.e("fuweida", "onIceCandidate");
        String sdp = iceCandidate.sdp;
        String sdpMid = iceCandidate.sdpMid;
        int sdpMLineIndex = iceCandidate.sdpMLineIndex;
        CandidateMessage candidateMessage = new CandidateMessage(sdp, sdpMLineIndex, sdpMid);

        try {
            MainActivity.sendMessage(this.peerId, candidateMessage.toJson().toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Log.e("fuweida", candidateMessage.toString());
    }

    @Override
    public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
        Log.e("fuweida", "onIceCandidatesRemoved");

    }

    @Override
    public void onAddStream(MediaStream mediaStream) {
        callbackMediaStream.addStream(mediaStream);
    }

    @Override
    public void onRemoveStream(MediaStream mediaStream) {

    }

    @Override
    public void onDataChannel(DataChannel dataChannel) {

    }

    @Override
    public void onRenegotiationNeeded() {

    }

    @Override
    public void onCreateSuccess(SessionDescription sessionDescription) {
        Log.e("fuweida", "onCreateSuccess");
        peerConnection.setLocalDescription(this, sessionDescription);
        String type = "";
        if (sessionDescription.type == SessionDescription.Type.OFFER) {
            type = "offer";
        } else if (sessionDescription.type == SessionDescription.Type.ANSWER) {
            type = "answer";
        }
        SdpMessage sdpMessage = new SdpMessage(type, sessionDescription.description);
        try {
            MainActivity.sendMessage(this.peerId, sdpMessage.toJson().toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Log.e("fuweida", sdpMessage.toString());
    }

    @Override
    public void onSetSuccess() {

    }

    @Override
    public void onCreateFailure(String s) {
        Log.e("fuweida", s);
    }

    @Override
    public void onSetFailure(String s) {
        Log.e("fuweda", s);
    }

    private String getStringKey(JSONObject jsonObject, String name){
        try {
            return jsonObject.getString(name);
        }catch (Exception e) {
            return null;
        }
    }

    private Integer getIntegerKey(JSONObject jsonObject, String name) {
        try {
            return jsonObject.getInt(name);
        }catch (Exception e) {
            return null;
        }
    }


    @Override
    public void message(int peer_id, String message) {
        this.peerId = peer_id;
        //Log.e("fuweicong", String.valueOf(peer_id));
        Log.d("fuweicong", message);
        try{
            JSONObject jsonObject  = new JSONObject(message);

            String sdp = getStringKey(jsonObject, "sdp");
            String type = getStringKey(jsonObject, "type");

            String candidate = getStringKey(jsonObject, "candidate");
            String sdpMin = getStringKey(jsonObject, "sdpMid");
            Integer sdpMLineIndex = getIntegerKey(jsonObject, "sdpMLineIndex");

            if (type != null) {
                initPeerConnection();
                SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, sdp);
                peerConnection.setRemoteDescription(this, sessionDescription);
                if(peerConnection.signalingState() == HAVE_REMOTE_OFFER) {
                    peerConnection.createAnswer(this, new MediaConstraints());
                } else {
                    Log.e("fuweicong", "HAVE_REMOTE_OFFER");
                }

                Log.e("fuweida", sdp);
            } else if (candidate != null && sdpMin != null && sdpMLineIndex != null){
                Log.e("fuweida", candidate);
                IceCandidate iceCandidate = new IceCandidate(sdpMin, sdpMLineIndex, candidate);
                peerConnection.addIceCandidate(iceCandidate);
            }
        }catch (Exception e) {
            Log.e("fuweicong", e.getMessage());
        }

    }
}
